Skip to content

Implement videos page#622

Open
SiddharthJiyani wants to merge 6 commits into
AOSSIE-Org:mainfrom
SiddharthJiyani:feat/videos-page-implementation
Open

Implement videos page#622
SiddharthJiyani wants to merge 6 commits into
AOSSIE-Org:mainfrom
SiddharthJiyani:feat/videos-page-implementation

Conversation

@SiddharthJiyani

@SiddharthJiyani SiddharthJiyani commented Nov 8, 2025

Copy link
Copy Markdown
Contributor

Feat: Implement Videos Page with Dynamic Thumbnails

Overview

Adds full video management functionality with an optimized approach that eliminates unnecessary thumbnail storage and provides automatic database cleanup.

Key Changes

Backend

  • Video database schema and CRUD operations
  • Video scanning from folders with metadata extraction
  • Auto-cleanup of deleted videos from database
  • Upload endpoint for video files

Frontend

  • Videos page with grid layout and sorting options
  • Plyr video player with controls and settings
  • Video card component with play overlay

Technical Details

  • Uses OpenCV for video metadata extraction (duration, dimensions, fps)
  • Plyr.js for enhanced video playback experience
  • Automatic file existence validation on every fetch
  • Clean codebase with removed deprecated functions

Testing

  • Verified video scanning from folders
  • Confirmed auto-cleanup of deleted videos
  • Tested video playback with Plyr controls
  • Validated UI responsiveness and interactions

Demo

video_page_implementation_.1.mp4

Summary by CodeRabbit

  • New Features

    • Full video management: browse, scan folders, cleanup, and upload via a new Videos page and API.
    • Responsive video gallery with sorting (Newest/Oldest, A–Z), thumbnail cards, and modal playback using a Plyr-based player.
    • Background scanning, metadata extraction, thumbnail support, and upload capability for video files.
  • Removed

    • Legacy Netflix-style custom player replaced by the new Plyr-based player.

@github-actions

github-actions Bot commented Nov 8, 2025

Copy link
Copy Markdown
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@coderabbitai

coderabbitai Bot commented Nov 8, 2025

Copy link
Copy Markdown
Contributor

PR changed again? Review this PR in Change Stack to compare snapshots and stay oriented.

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds end-to-end video support: backend config, DB/table creation, folder integration, video utils and routes; frontend API clients, Plyr player, VideoCard, Videos page, and build config for Plyr.

Changes

Videos feature

Layer / File(s) Summary
Backend config & startup
backend/app/config/settings.py, backend/main.py
Adds VIDEOS_PATH and THUMBNAIL_VIDEOS_PATH; imports videos router and DB initializer; calls db_create_videos_table() during lifespan and mounts the /videos router.
Folder routes: invoke video processing
backend/app/routes/folders.py
Imports video_util_process_folder_videos and calls it in post_folder_add_sequence and post_sync_folder_sequence after image processing.
Frontend: deps, endpoints, and exports
frontend/package.json, frontend/vite.config.ts, frontend/src/api/apiEndpoints.ts, frontend/src/api/api-functions/index.ts, frontend/src/routes/AppRoutes.tsx
Adds plyr dependency and Vite optimizeDeps; defines videosEndpoints; re-exports videos API functions; imports and routes the Videos page.
Frontend: types, UI, and player
frontend/src/types/Media.ts, frontend/src/pages/VideosPage/Videos.tsx, frontend/src/components/Media/VideoCard.tsx, frontend/src/components/VideoPlayer/PlyrPlayer.tsx
Adds VideoMetadata and Video types; adds Videos page (fetch/scan/refresh/sort, modal player); new VideoCard component; adds PlyrPlayer replacing the removed Netflix-style player.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Frontend as Videos Page
  participant API as /videos
  participant Utils as video_util_process_folder_videos
  participant DB as VideosDB
  participant FS as FileSystem

  User->>Frontend: open Videos page
  Frontend->>API: GET /videos/
  API->>DB: db_get_all_videos()
  DB-->>API: videos list
  API-->>Frontend: GetAllVideosResponse

  alt scan requested or no videos
    Frontend->>API: POST /videos/scan
    API->>DB: db_get_all_folder_details()
    API->>Utils: video_util_process_folder_videos(folder_data)
    Utils->>FS: scan folders & read files
    loop per file
      Utils->>Utils: extract metadata (OpenCV)
      Utils->>DB: db_insert_video(record)
    end
    API->>DB: db_get_all_videos()
    API-->>Frontend: updated GetAllVideosResponse
  end

  User->>Frontend: click VideoCard
  Frontend->>Frontend: init PlyrPlayer(src)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels: Python, TypeScript/JavaScript

Suggested reviewers:

  • rahulharpal1603

🐇
I hop through folders, tails a-flutter,
Thumbnails gleam and players stutter.
Plyr wakes up with buttery glide,
Videos arrive — a joyful ride.
Hop, click, and play — the updates flutter!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Implement videos page' directly and clearly summarizes the main objective of the changeset, which adds comprehensive video management functionality across backend and frontend.
Linked Issues check ✅ Passed The PR successfully addresses all core requirements from issue #611: backend API endpoints for video management [#611], video scanning with metadata extraction [#611], automatic cleanup [#611], frontend Videos page UI [#611], video cards with playback [#611], and API integration [#611].
Out of Scope Changes check ✅ Passed All changes align with issue #611 objectives. Minor changes to existing files (button.tsx cursor styling, vite.config.ts Plyr optimization) are supporting additions directly enabling video playback functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (11)
frontend/src/components/ui/button.tsx (1)

8-8: Good UX improvement with cursor-pointer, but disabled:cursor-not-allowed is redundant.

The cursor-pointer addition improves button UX by indicating clickability. However, disabled:cursor-not-allowed has no effect because disabled:pointer-events-none is already set—when pointer events are disabled, the browser won't display custom cursor styles.

If you want to keep the class string cleaner, you can remove the redundant class:

-  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
backend/app/schemas/videos.py (1)

5-13: Consider adding field validators for VideoMetadata.

The VideoMetadata model lacks validation constraints. Consider adding validators to ensure data integrity (e.g., positive values for width, height, duration, and file_size).

Example validation:

from pydantic import BaseModel, Field

class VideoMetadata(BaseModel):
    name: str
    date_created: Optional[str] = None
    width: int = Field(gt=0)
    height: int = Field(gt=0)
    duration: float = Field(gt=0)
    file_location: str
    file_size: int = Field(ge=0)
    item_type: str
backend/app/config/settings.py (1)

27-29: Consider using absolute paths for video storage.

The relative paths ./videos and ./videos/thumbnails depend on the current working directory. While this is consistent with the existing IMAGES_PATH pattern, consider using pathlib.Path with absolute paths or environment variables for more robust path handling across different execution contexts.

frontend/src/types/Media.ts (1)

40-48: Consider nullable thumbnailPath for backend consistency.

The thumbnailPath field is typed as string (line 43), but the backend VideoData.thumbnailPath is Optional[str]. Consider using thumbnailPath: string | null to handle cases where thumbnails might not be available.

 export interface Video {
   id: string;
   path: string;
-  thumbnailPath: string;
+  thumbnailPath: string | null;
   folder_id?: string;
   metadata?: VideoMetadata;
   title?: string;
   tags?: string[];
 }
backend/app/routes/folders.py (2)

71-73: Consider checking video processing return value.

While video_util_process_folder_videos returns a boolean indicating success/failure, the return value is not checked. If video processing fails, the function continues silently. Consider logging the result or adding explicit error handling for better observability.

 # Process images and videos in all folders
 image_util_process_folder_images(folder_data)
-video_util_process_folder_videos(folder_data)
+video_success = video_util_process_folder_videos(folder_data)
+if not video_success:
+    logger.warning(f"Video processing encountered errors for folder {folder_path}")

119-121: Consider checking video processing return value.

Similar to the add folder sequence, the return value of video_util_process_folder_videos is not checked here. Consider adding result validation for consistency and better error visibility.

 # Process images and videos in all folders
 image_util_process_folder_images(folder_data)
-video_util_process_folder_videos(folder_data)
+video_success = video_util_process_folder_videos(folder_data)
+if not video_success:
+    logger.warning(f"Video processing encountered errors during sync for folder {folder_path}")
 image_util_process_untagged_images()
frontend/src/components/VideoPlayer/PlyrPlayer.tsx (1)

102-102: Consider supporting multiple video formats.

The video source is hardcoded to type="video/mp4", which may not support other formats like WebM or OGV.

If you need to support multiple formats, consider:

-        <source src={src} type="video/mp4" />
+        <source src={src} />

Or detect the format from the file extension and set the appropriate MIME type.

backend/app/routes/videos.py (1)

24-33: Consider extracting duplicate VideoData mapping logic.

The same VideoData construction pattern appears in all four endpoints. Extracting this into a helper function would improve maintainability.

Example:

def _map_videos_to_response(videos: List[dict]) -> List[VideoData]:
    """Helper to map database video dicts to VideoData schema."""
    return [
        VideoData(
            id=v["id"],
            path=v["path"],
            folder_id=v.get("folder_id"),
            thumbnailPath=v["thumbnailPath"],
            metadata=image_util_parse_metadata(v.get("metadata")),
        )
        for v in videos
    ]

# Then use in endpoints:
data = _map_videos_to_response(videos)

Also applies to: 57-66, 100-109, 133-142

backend/app/utils/videos.py (3)

55-81: Consider refactoring the import and directory check.

Two minor inefficiencies:

  1. Import inside function (line 59): The local import of db_get_video_by_path suggests a circular dependency issue. Consider restructuring imports or using TYPE_CHECKING to resolve this more cleanly.

  2. Redundant directory check (line 61): video_util_ensure_dirs() is called for every file registration. While harmless (due to exist_ok=True), it's unnecessary overhead. Consider calling it once during application startup or at the beginning of batch operations like video_util_process_folder_videos.

For the import issue, consider moving it to the top with TYPE_CHECKING:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from app.database.videos import db_get_video_by_path

Or restructure the module dependencies to eliminate the circular import.


90-108: Add consistent error handling for recursive mode.

The non-recursive mode catches OSError (line 106), but the recursive mode using os.walk (lines 94-99) doesn't. The os.walk function can raise OSError for permission issues or other filesystem errors when traversing subdirectories, leading to inconsistent behavior.

Apply this diff to add consistent error handling:

 def video_util_get_videos_from_folder(
     folder_path: str, recursive: bool = True
 ) -> List[str]:
     videos: List[str] = []
     if recursive:
-        for root, _, files in os.walk(folder_path):
-            for f in files:
-                p = os.path.join(root, f)
-                if video_util_is_valid_video(p):
-                    videos.append(p)
+        try:
+            for root, _, files in os.walk(folder_path):
+                for f in files:
+                    p = os.path.join(root, f)
+                    if video_util_is_valid_video(p):
+                        videos.append(p)
+        except OSError:
+            pass
     else:
         try:
             for f in os.listdir(folder_path):
                 p = os.path.join(folder_path, f)
                 if os.path.isfile(p) and video_util_is_valid_video(p):
                     videos.append(p)
         except OSError:
             pass
     return videos

114-126: Add logging for top-level exceptions.

The top-level exception handler (lines 125-126) silently returns False without logging the error. This makes it difficult to diagnose why folder processing failed, especially since the function processes multiple folders and could fail for various reasons (filesystem issues, database errors, etc.).

Apply this diff to add error logging:

 def video_util_process_folder_videos(folder_data: List[essential_tuple]) -> bool:
     try:
         video_util_ensure_dirs()
         for path, folder_id, recursive in folder_data:
             file_list = video_util_get_videos_from_folder(path, recursive)
             for fp in file_list:
                 try:
                     video_util_register_file(fp, folder_id)
                 except Exception:
                     continue
         return True
-    except Exception:
+    except Exception as e:
+        logger.error(f"Failed to process folder videos: {e}")
         return False
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe2f98d and f1232dc.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • backend/app/config/settings.py (1 hunks)
  • backend/app/database/videos.py (1 hunks)
  • backend/app/routes/folders.py (3 hunks)
  • backend/app/routes/videos.py (1 hunks)
  • backend/app/schemas/videos.py (1 hunks)
  • backend/app/utils/videos.py (1 hunks)
  • backend/main.py (3 hunks)
  • frontend/package.json (1 hunks)
  • frontend/src/api/api-functions/index.ts (1 hunks)
  • frontend/src/api/api-functions/videos.ts (1 hunks)
  • frontend/src/api/apiEndpoints.ts (1 hunks)
  • frontend/src/components/Media/VideoCard.tsx (1 hunks)
  • frontend/src/components/VideoPlayer/NetflixStylePlayer.tsx (0 hunks)
  • frontend/src/components/VideoPlayer/PlyrPlayer.tsx (1 hunks)
  • frontend/src/components/ui/button.tsx (1 hunks)
  • frontend/src/pages/VideosPage/Videos.tsx (1 hunks)
  • frontend/src/routes/AppRoutes.tsx (1 hunks)
  • frontend/src/types/Media.ts (2 hunks)
  • frontend/vite.config.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/src/components/VideoPlayer/NetflixStylePlayer.tsx
🧰 Additional context used
🧬 Code graph analysis (10)
frontend/src/api/api-functions/videos.ts (3)
frontend/src/types/API.ts (1)
  • APIResponse (1-8)
frontend/src/api/axiosConfig.ts (1)
  • apiClient (5-12)
frontend/src/api/apiEndpoints.ts (1)
  • videosEndpoints (5-9)
frontend/src/types/Media.ts (1)
backend/app/schemas/videos.py (1)
  • VideoMetadata (5-13)
frontend/src/routes/AppRoutes.tsx (1)
frontend/src/constants/routes.ts (1)
  • ROUTES (1-11)
frontend/src/pages/VideosPage/Videos.tsx (6)
frontend/src/types/API.ts (1)
  • APIResponse (1-8)
frontend/src/api/api-functions/videos.ts (2)
  • fetchAllVideos (5-10)
  • scanVideos (25-28)
frontend/src/types/Media.ts (1)
  • Video (40-48)
frontend/src/components/ui/LoadingScreen/LoadingScreen.tsx (1)
  • LoadingScreen (15-64)
frontend/src/components/Media/VideoCard.tsx (1)
  • VideoCard (13-52)
frontend/src/components/VideoPlayer/PlyrPlayer.tsx (1)
  • PlyrPlayer (10-106)
backend/app/schemas/videos.py (1)
frontend/src/types/Media.ts (1)
  • VideoMetadata (13-22)
backend/app/utils/videos.py (1)
backend/app/database/videos.py (2)
  • db_insert_video (52-75)
  • db_get_video_by_path (128-156)
backend/app/routes/videos.py (5)
backend/app/database/videos.py (1)
  • db_get_all_videos (78-125)
backend/app/schemas/videos.py (3)
  • GetAllVideosResponse (24-27)
  • ErrorResponse (30-33)
  • VideoData (16-21)
backend/app/utils/images.py (1)
  • image_util_parse_metadata (496-513)
backend/app/utils/videos.py (3)
  • video_util_register_file (55-81)
  • video_util_ensure_dirs (15-16)
  • video_util_process_folder_videos (114-126)
backend/app/database/folders.py (1)
  • db_get_all_folder_details (397-418)
frontend/src/components/Media/VideoCard.tsx (1)
frontend/src/types/Media.ts (1)
  • Video (40-48)
backend/app/routes/folders.py (2)
backend/app/utils/videos.py (1)
  • video_util_process_folder_videos (114-126)
backend/app/utils/images.py (1)
  • image_util_process_folder_images (32-84)
backend/main.py (1)
backend/app/database/videos.py (1)
  • db_create_videos_table (31-49)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: sync-labels
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
🔇 Additional comments (18)
frontend/src/routes/AppRoutes.tsx (1)

11-11: LGTM!

The Videos component import and route registration follow the existing patterns and integrate cleanly with the route structure.

Also applies to: 19-19

frontend/vite.config.ts (1)

17-19: LGTM!

Pre-bundling plyr in optimizeDeps is the recommended approach for improving dev server startup time and handling potential ESM compatibility issues with the library.

frontend/src/api/api-functions/index.ts (1)

4-4: LGTM!

The videos module export follows the established pattern for API function organization.

frontend/src/types/Media.ts (1)

13-22: LGTM!

The VideoMetadata interface correctly mirrors the backend schema fields and types, ensuring consistent data shape across frontend and backend.

backend/app/routes/folders.py (1)

45-45: LGTM!

The import of video processing utilities integrates cleanly with the existing folder processing infrastructure.

frontend/package.json (1)

56-56: plyr 3.8.3 is current and secure.

Version 3.8.3 is the latest release with no known security vulnerabilities or CVEs. The package was released August 27, 2025, indicating active maintenance. No action needed.

backend/app/schemas/videos.py (1)

21-21: The code is correct for the project's Python version requirement.

The union syntax Mapping[str, Any] | VideoMetadata is appropriate here. The project's GitHub Actions workflows explicitly specify Python 3.12, which fully supports PEP 604 union syntax (available since Python 3.10). No changes are needed.

frontend/src/api/apiEndpoints.ts (1)

5-9: LGTM!

The new videosEndpoints object follows the established pattern and provides clear endpoint definitions for video operations.

backend/main.py (1)

22-22: LGTM!

The video module integration follows the established pattern for other entities in the application. The table creation is correctly placed in the startup sequence, and the router is properly mounted with appropriate tags.

Also applies to: 28-28, 58-58, 132-132

frontend/src/api/api-functions/videos.ts (1)

5-28: LGTM!

The API functions follow a clean pattern and leverage the existing apiClient configuration. Error handling appears to be delegated to the caller (React Query), which is a reasonable approach for this architecture.

frontend/src/pages/VideosPage/Videos.tsx (1)

68-141: LGTM!

The UI implementation is well-structured with responsive grid layout, sorting controls, and a modal player. The component provides a good user experience with loading states and empty states.

frontend/src/components/Media/VideoCard.tsx (1)

13-52: Well-structured video card component.

The component provides good UX with hover effects, play button overlay, and responsive design. The video metadata fallback chain is robust.

frontend/src/components/VideoPlayer/PlyrPlayer.tsx (1)

10-106: Excellent player implementation with proper cleanup.

The component handles Plyr initialization, configuration, and cleanup correctly. The dynamic import approach and effect dependencies are appropriate.

backend/app/database/videos.py (4)

31-49: LGTM!

The table schema is well-designed with appropriate constraints. The foreign key relationship with ON DELETE SET NULL ensures data integrity when folders are removed.


52-76: LGTM!

The upsert logic using ON CONFLICT is appropriate for handling duplicate paths, and error handling with rollback is properly implemented.


78-126: Excellent auto-cleanup implementation.

The automatic cleanup of database entries for deleted files is a smart approach that keeps the database in sync with the filesystem. The implementation is safe and well-logged.


128-171: LGTM!

Both query and delete functions are well-implemented with proper error handling and connection management.

backend/app/utils/videos.py (1)

15-16: Now let me search for broader "thumbnail" references in the codebase:

Remove THUMBNAIL_VIDEOS_PATH from settings or implement directory creation.

The constant is defined in backend/app/config/settings.py (line 29) but never used anywhere in the codebase. Since the PR optimizes by avoiding unnecessary thumbnails (line 77 sets thumbnailPath to None), either remove this unused constant from settings to prevent confusion, or add os.makedirs(THUMBNAIL_VIDEOS_PATH, exist_ok=True) to video_util_ensure_dirs() if thumbnail functionality is planned for the future.

Comment thread backend/app/routes/videos.py Outdated
Comment thread backend/app/routes/videos.py Outdated
Comment thread backend/app/utils/videos.py
Comment thread frontend/src/components/Media/VideoCard.tsx
Comment thread frontend/src/components/VideoPlayer/PlyrPlayer.tsx Outdated
Comment thread frontend/src/pages/VideosPage/Videos.tsx
Comment thread frontend/src/pages/VideosPage/Videos.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
backend/app/routes/videos.py (4)

9-9: Consider adding tags to the router for better API documentation.

Adding tags helps organize endpoints in the automatically generated OpenAPI/Swagger documentation.

Apply this diff:

-router = APIRouter()
+router = APIRouter(tags=["videos"])

12-39: Consider pagination for scalability.

The endpoint returns all videos without pagination, which could cause performance issues and large response sizes with extensive video libraries.

Consider adding pagination parameters:

@router.get(
    "/",
    response_model=GetAllVideosResponse,
    responses={500: {"model": ErrorResponse}},
)
def get_all_videos(skip: int = 0, limit: int = 100):
    try:
        videos = db_get_all_videos()
        # Apply pagination
        paginated_videos = videos[skip:skip + limit]
        data: List[VideoData] = [
            VideoData(
                id=v["id"],
                path=v["path"],
                folder_id=v.get("folder_id"),
                thumbnailPath=v["thumbnailPath"],
                metadata=image_util_parse_metadata(v.get("metadata")),
            )
            for v in paginated_videos
        ]
        return GetAllVideosResponse(
            success=True, 
            message=f"Retrieved {len(data)} videos (showing {skip}-{skip+len(data)} of {len(videos)} total)", 
            data=data
        )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=ErrorResponse(
                success=False, error="Internal server error", message=str(e)
            ).model_dump(),
        )

47-47: Remove async keyword or use asynchronous operations.

The function is declared as async but doesn't use any await expressions. This creates unnecessary overhead and inconsistency with get_all_videos, which is synchronous.

Apply this diff:

-async def scan_videos_from_folders():
+def scan_videos_from_folders():

80-80: Remove async keyword or use asynchronous operations.

Similar to scan_videos_from_folders, this function is declared as async but doesn't use any await expressions, creating inconsistency with get_all_videos.

Apply this diff:

-async def cleanup_deleted_videos():
+def cleanup_deleted_videos():
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1232dc and 07839fe.

📒 Files selected for processing (1)
  • backend/app/routes/videos.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/app/routes/videos.py (5)
backend/app/database/videos.py (1)
  • db_get_all_videos (78-125)
backend/app/schemas/videos.py (3)
  • GetAllVideosResponse (24-27)
  • ErrorResponse (30-33)
  • VideoData (16-21)
backend/app/utils/images.py (1)
  • image_util_parse_metadata (496-513)
backend/app/utils/videos.py (1)
  • video_util_process_folder_videos (114-126)
backend/app/database/folders.py (1)
  • db_get_all_folder_details (397-418)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (windows-latest)
🔇 Additional comments (3)
backend/app/routes/videos.py (3)

80-81: LGTM! The cleanup mechanism works via implicit behavior.

The endpoint correctly leverages the automatic cleanup behavior in db_get_all_videos, which removes database entries for videos whose files no longer exist (as shown in the relevant code snippet from backend/app/database/videos.py). The docstring clearly explains the purpose.


33-39: LGTM! Error handling is consistent and appropriate.

The error handling pattern correctly uses ErrorResponse.model_dump() to convert the Pydantic model to a dictionary for the HTTPException detail field. The consistent structure across all endpoints makes the API predictable.

Also applies to: 66-72, 99-105


50-50: Review comment is incorrect—AI_Tagging and recursive scanning are unrelated features.

The hardcoded recursive=False is intentional system-wide behavior (replicated in backend/app/routes/folders.py:68 with an explicit comment). The database schema has no recursive field; it's not configurable per folder.

AI_Tagging is a separate per-folder feature flag for AI-based image tagging (queried in backend/app/database/images.py), not a control for scan recursion. It does not indicate or require recursive folder scanning.

Likely an incorrect or invalid review comment.

Comment thread backend/app/routes/videos.py Outdated
@rahulharpal1603

Copy link
Copy Markdown
Contributor

@SiddharthJiyani Please resolve conflicts.

@github-actions github-actions Bot added backend enhancement New feature or request frontend labels Nov 15, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 809a5e6 and 8a8033f.

📒 Files selected for processing (3)
  • frontend/src/api/apiEndpoints.ts (1 hunks)
  • frontend/src/routes/AppRoutes.tsx (1 hunks)
  • frontend/src/types/Media.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/api/apiEndpoints.ts
  • frontend/src/routes/AppRoutes.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/types/Media.ts (1)
backend/app/schemas/videos.py (1)
  • VideoMetadata (5-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (windows-latest)
🔇 Additional comments (2)
frontend/src/types/Media.ts (2)

13-22: LGTM! VideoMetadata interface aligns with backend schema.

The VideoMetadata interface correctly mirrors the backend schema with appropriate TypeScript types and includes the essential duration field for video playback.


24-34: The review comment is incorrect.

The git diff shows that folder_id: string was already required in the codebase before this PR and was not modified. The line appears as unchanged context in the diff, not as a new addition or modification. The actual changes in this PR are:

  • Added VideoMetadata interface
  • Added isFavourite?: boolean to the Image interface
  • Added Video interface with optional folder_id?: string
  • Added images: Image[] property to MediaViewProps

There is no breaking change to Image.folder_id in this PR.

Likely an incorrect or invalid review comment.

Comment thread frontend/src/types/Media.ts
@SiddharthJiyani

Copy link
Copy Markdown
Contributor Author

@SiddharthJiyani Please resolve conflicts.

Done

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR still has unresolved merge conflicts and has been inactive for the past 30 days.

The PR has merge conflicts label is still applied, and there has been no recent activity on this PR.

Please resolve the merge conflicts or leave a comment if you are still actively working on it.

If there is no further activity within the next 5 days, this PR may be automatically closed to help keep the PR backlog manageable.

Copilot AI review requested due to automatic review settings June 6, 2026 07:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds first-class video support across frontend and backend: listing videos, scanning folders for videos, and playing them in-app using Plyr.

Changes:

  • Frontend: adds Videos page/route, video card UI, and Plyr-based player (plus plyr dependency).
  • Backend: adds videos table + CRUD helpers, video metadata extraction/registration utilities, and /videos routes (list/scan/cleanup).
  • Folder workflows updated to process videos alongside images.

Reviewed changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
frontend/vite.config.ts Pre-bundles plyr to avoid dev-time optimization issues.
frontend/src/types/Media.ts Introduces Video / VideoMetadata types.
frontend/src/routes/AppRoutes.tsx Routes /videos to the new Videos page.
frontend/src/pages/VideosPage/Videos.tsx Implements videos listing, sorting, scanning fallback, and modal playback.
frontend/src/components/ui/button.tsx Adjusts cursor/disabled cursor behavior for buttons.
frontend/src/components/VideoPlayer/PlyrPlayer.tsx Adds Plyr wrapper component and loads Plyr dynamically.
frontend/src/components/VideoPlayer/NetflixStylePlayer.tsx Removes old custom video player implementation.
frontend/src/components/Media/VideoCard.tsx Adds a clickable video preview card.
frontend/src/api/apiEndpoints.ts Adds videos API endpoint constants.
frontend/src/api/api-functions/videos.ts Adds fetch/upload/scan API helpers for videos.
frontend/src/api/api-functions/index.ts Re-exports videos API helpers.
frontend/package.json Adds plyr dependency.
backend/main.py Initializes videos table and registers videos router; starts sync microservice.
backend/app/utils/videos.py Implements video scanning, validation, metadata extraction, and DB registration.
backend/app/schemas/videos.py Adds Pydantic schemas for video responses.
backend/app/routes/videos.py Adds /videos routes: list, scan, cleanup.
backend/app/routes/folders.py Processes videos when folders are added/synced.
backend/app/database/videos.py Adds videos table and DB operations, plus cleanup of missing files.
backend/app/config/settings.py Adds videos storage paths.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import { AITagging } from '@/pages/AITagging/AITagging';
import { PersonImages } from '@/pages/PersonImages/PersonImages';
import { ComingSoon } from '@/pages/ComingSoon/ComingSoon';
import Videos from '@/pages/VideosPage/Videos';
<Route element={<Layout />}>
<Route path={ROUTES.HOME} element={<Home />} />
<Route path={ROUTES.VIDEOS} element={<ComingSoon />} />
<Route path={ROUTES.VIDEOS} element={<Videos />} />
Comment on lines 145 to 146
);
};
Comment on lines +30 to +49
useEffect(() => {
const run = async () => {
if (data?.success && Array.isArray(data.data)) {
const list = data.data as unknown as Video[];
setVideos(list);
if (list.length === 0) {
try {
const scanned = await scanVideos();
if (scanned?.success && Array.isArray(scanned.data)) {
setVideos(scanned.data as unknown as Video[]);
}
} catch (error) {
console.error('Failed to scan videos:', error);
// Videos will remain empty, showing "No videos found" message
}
}
}
};
run();
}, [data]);
Comment on lines +90 to +99
<video
ref={ref}
playsInline
crossOrigin="anonymous"
poster={poster}
controls
className="h-full w-full"
>
<source src={src} type="video/mp4" />
</video>
Comment on lines +1 to +6
import os
import uuid
import json
import datetime
import cv2
from typing import Dict, List, Tuple
Comment on lines +114 to +126
def video_util_process_folder_videos(folder_data: List[essential_tuple]) -> bool:
try:
video_util_ensure_dirs()
for path, folder_id, recursive in folder_data:
file_list = video_util_get_videos_from_folder(path, recursive)
for fp in file_list:
try:
video_util_register_file(fp, folder_id)
except Exception:
continue
return True
except Exception:
return False
Comment on lines +102 to +110
videos.append(
{
"id": v_id,
"path": path,
"folder_id": str(folder_id) if folder_id is not None else "",
"thumbnailPath": thumb,
"metadata": metadata,
}
)
Comment on lines +4 to +5
from app.schemas.videos import GetAllVideosResponse, ErrorResponse, VideoData
from app.utils.images import image_util_parse_metadata
Comment on lines +12 to +21
def _map_videos_to_response(videos: List[dict]) -> List[VideoData]:
"""Helper function to map database video dicts to VideoData objects."""
return [
VideoData(
id=v["id"],
path=v["path"],
folder_id=v.get("folder_id"),
thumbnailPath=v["thumbnailPath"],
metadata=image_util_parse_metadata(v.get("metadata")),
)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/main.py`:
- Around line 169-170: The server is currently bound to localhost
(host="localhost") which prevents external/Docker/LAN access; change the binding
to allow external access by default (for example use 0.0.0.0) or make the host
configurable via an environment variable (e.g., BIND_HOST) and default to an
externally reachable address, updating the host="localhost" occurrences in
backend/main.py (the server start call) and add a short comment documenting if
loopback-only binding is intentional.
- Around line 49-50: When creating the DB parent directory, guard against an
empty dirname: compute path = os.path.dirname(DATABASE_PATH) and only call
os.makedirs(path, exist_ok=True) when path is non-empty (e.g., if path). Update
the block that uses DATABASE_PATH and os.path.dirname to skip os.makedirs when
dirname returns "" so startup won't call os.makedirs on an empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4c5c8595-5f9d-4cd9-8897-06ed11ce63ae

📥 Commits

Reviewing files that changed from the base of the PR and between 8a8033f and 34d7429.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • backend/app/config/settings.py
  • backend/app/routes/folders.py
  • backend/main.py
  • frontend/package.json
  • frontend/src/api/api-functions/index.ts
  • frontend/src/api/apiEndpoints.ts
  • frontend/src/routes/AppRoutes.tsx
  • frontend/src/types/Media.ts
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/api/api-functions/index.ts
  • frontend/package.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/routes/AppRoutes.tsx
  • frontend/src/api/apiEndpoints.ts
  • backend/app/routes/folders.py
  • backend/app/config/settings.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/main.py`:
- Around line 169-170: The server is currently bound to localhost
(host="localhost") which prevents external/Docker/LAN access; change the binding
to allow external access by default (for example use 0.0.0.0) or make the host
configurable via an environment variable (e.g., BIND_HOST) and default to an
externally reachable address, updating the host="localhost" occurrences in
backend/main.py (the server start call) and add a short comment documenting if
loopback-only binding is intentional.
- Around line 49-50: When creating the DB parent directory, guard against an
empty dirname: compute path = os.path.dirname(DATABASE_PATH) and only call
os.makedirs(path, exist_ok=True) when path is non-empty (e.g., if path). Update
the block that uses DATABASE_PATH and os.path.dirname to skip os.makedirs when
dirname returns "" so startup won't call os.makedirs on an empty string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4c5c8595-5f9d-4cd9-8897-06ed11ce63ae

📥 Commits

Reviewing files that changed from the base of the PR and between 8a8033f and 34d7429.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • backend/app/config/settings.py
  • backend/app/routes/folders.py
  • backend/main.py
  • frontend/package.json
  • frontend/src/api/api-functions/index.ts
  • frontend/src/api/apiEndpoints.ts
  • frontend/src/routes/AppRoutes.tsx
  • frontend/src/types/Media.ts
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/api/api-functions/index.ts
  • frontend/package.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/routes/AppRoutes.tsx
  • frontend/src/api/apiEndpoints.ts
  • backend/app/routes/folders.py
  • backend/app/config/settings.py
🛑 Comments failed to post (2)
backend/main.py (2)

49-50: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard empty parent directory before makedirs

At Line 49-50, os.path.dirname(DATABASE_PATH) may be "" (e.g., filename-only DB path), which makes os.makedirs("") fail during startup.

Suggested fix
-path = os.path.dirname(DATABASE_PATH)
-os.makedirs(path, exist_ok=True)
+path = os.path.dirname(DATABASE_PATH)
+if path:
+    os.makedirs(path, exist_ok=True)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 49 - 50, When creating the DB parent directory,
guard against an empty dirname: compute path = os.path.dirname(DATABASE_PATH)
and only call os.makedirs(path, exist_ok=True) when path is non-empty (e.g., if
path). Update the block that uses DATABASE_PATH and os.path.dirname to skip
os.makedirs when dirname returns "" so startup won't call os.makedirs on an
empty string.

169-170: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid binding only to loopback unless intentionally local-only

At Line 169-170 (and reflected in Line 93), binding to localhost restricts access to the same host. This can break Docker/LAN access and cross-service integration if the backend is expected to be reachable externally.

Suggested fix
-        host="localhost",
+        host="0.0.0.0",
-        {"url": "http://localhost:52123", "description": "Local Development server"}
+        {"url": "http://0.0.0.0:52123", "description": "Local Development server"}

If local-only binding is intentional, document that explicitly in this file to prevent deployment regressions.

Also applies to: 93-93

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 169 - 170, The server is currently bound to
localhost (host="localhost") which prevents external/Docker/LAN access; change
the binding to allow external access by default (for example use 0.0.0.0) or
make the host configurable via an environment variable (e.g., BIND_HOST) and
default to an externally reachable address, updating the host="localhost"
occurrences in backend/main.py (the server start call) and add a short comment
documenting if loopback-only binding is intentional.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat: Implement Videos Page

3 participants